package com.moleq.base;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.media.AudioManager;
import android.media.SoundPool;
import android.media.SoundPool.OnLoadCompleteListener;
import android.os.Handler;
import android.util.Log;

/**
 * Acknowledge Beep for Touching Button ボタン確認音
 * 
 * @author tanaka-ta
 * 
 */
public class SmartBeep {
	private static final String LOGTAG = "SmartBeep";
	private static final int DELAY = 500;
	private SoundPool soundPool;
	private int loadComplete;

	private static SmartBeep sInstance = new SmartBeep();
	private Context mContext;

	private float mSystemVolume; // Media Volume
	private static final int VOLUME_RATE = 50; // Percent

	public static final String SOUND_CLICK = "click";
	public static final String SOUND_ERROR = "error";
	public static final String SOUND_SCAN = "scan";
	// private static final String soundNames[] = {SOUND_CLICK, SOUND_ERROR};
	private static final String soundNames[] = { SOUND_CLICK, SOUND_ERROR, SOUND_SCAN };

	private Map<String, Integer> soundID = new HashMap<String, Integer>();

	/**
	 * Get the SmartBeep object. SmartBeepの唯一のオブジェクトを取得する。
	 * 
	 * @return is the SmartBeep object defined as singleton.
	 */
	public static SmartBeep getInstance() {
		return sInstance;
	}

	private SmartBeep() {

	}

	/**
	 * Prepare AudioTrack data to play.　Please call this method every Activity
	 * restoration.
	 * 
	 * @param context
	 *            is from Activity.
	 */
	public void reload(Context context) {
		mContext = context;

		dispose();

		// Volume tuning
		AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
		int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
		int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
		mSystemVolume = (float) currentVolume / maxVolume;
		if (currentVolume > 1) {
			mSystemVolume = mSystemVolume * VOLUME_RATE / 100; // turn volume
																// down
		}

		soundPool = new SoundPool(soundNames.length, AudioManager.STREAM_MUSIC, 0);
		soundPool.setOnLoadCompleteListener(new OnLoadCompleteListener() {
			@Override
			public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
				if (status != 0) {
					Log.v("SmartBeep", "Load data error");
					return;
				}
				soundPool.setVolume(sampleId, mSystemVolume, mSystemVolume);
				loadComplete++;
			}
		});

		for (String soundName : soundNames) {
			soundID.put(soundName, getSoundId(mContext, soundPool, soundName));
		}
	}

	/**
	 * Play beep sound. ビープ音を再生する。事前にreload関数呼び出しが必要。
	 */
	public void play() {
		play(SOUND_CLICK);
	}

	public void play(String sound) {
		if (soundPool == null) {
			return;
		}
		if (mSystemVolume == 0) {
			return;
		}
		if (loadComplete < soundNames.length) {
			final String delayedSound = sound;
			Handler handler = new Handler();
			Runnable runnable = new Runnable() {
				@Override
				public void run() {
					doPlay(delayedSound);
				}
			};
			handler.postDelayed(runnable, DELAY);
			return;
		}
		doPlay(sound);
	}

	private void doPlay(String sound) {
		if (soundID == null) {
			return;
		}
		if (mSystemVolume == 0) {
			return;
		}
		if (loadComplete < soundNames.length) {
			Log.v("SmartBeep", "Load not completed");
			return;
		}

		Integer id = soundID.get(sound);
		if (id == null) {
			return;
		}

		soundPool.play(id, mSystemVolume, mSystemVolume, 0, 0, 1.0F);
	}

	/**
	 * Stop beep sound. ビープ音を停止する。
	 */
	public void stop() {
		if (soundPool == null) {
			return;
		}
		if (loadComplete == 0) {
			return;
		}

		Iterator<Integer> it = soundID.values().iterator();
		while (it.hasNext()) {
			soundPool.stop(it.next());
		}
	}

	/**
	 * Dispose AudioTrack resource. Do not call this method in
	 * Activity.onPause() which is not synchronized with AudioTrack.play().
	 * AudioTrackの内部バッファを破棄する
	 * 。このメソッドはActivityのonPause()では呼びださないでください。音が鳴る前にActivityが破棄されることがあります。
	 */
	public void dispose() {
		if (soundPool == null) {
			return;
		}

		Iterator<Integer> it = soundID.values().iterator();
		while (it.hasNext()) {
			int eachSound = it.next();
			soundPool.stop(eachSound);
			soundPool.unload(eachSound);
		}

		soundPool.release();

		loadComplete = 0;
		soundPool = null;
	}

	/**
	 * Get specified SoundPool ID from Common resource sound. If this method can
	 * not find the sound, it response -1.
	 * 
	 * @param cotext
	 *            Application context.
	 * @param soundPool
	 *            Valid SoundPool object.
	 * @param name
	 *            Sound name
	 * @return SoundPool ID
	 */
	private int getSoundIdOrg(Context cotext, SoundPool soundPool, String name) {
		final String mResouceName = "jp.co.casio.caios.component.common.resource";

		Context context = null;
		int soundID = -1;
		int resourceID = 0;
		try {
			context = cotext.createPackageContext(mResouceName, Context.CONTEXT_IGNORE_SECURITY);
		} catch (NameNotFoundException e) {
			Log.w(LOGTAG, "Can not find common resource");
			e.printStackTrace();
		}

		if (context != null) {
			Resources res = context.getResources();
			String path = "raw/" + name;
			resourceID = res.getIdentifier(path, null, mResouceName);
			if (resourceID == 0) {
				return soundID;
			}
			soundID = soundPool.load(context, resourceID, 1);
		}
		return soundID;
	}

	private int getSoundId(Context context, SoundPool soundPool, String name) {

		final String mResouceName = context.getPackageName();

		int soundID = -1;
		int resourceID = 0;

		if (context != null) {
			Resources res = context.getResources();
			String path = "raw/" + name;
			resourceID = res.getIdentifier(path, null, mResouceName);
			if (resourceID == 0) {
				return soundID;
			}
			soundID = soundPool.load(context, resourceID, 1);
		}
		return soundID;
	}

}
